package com.wmx.hb.controller;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author wangMaoXiong
 * @version 1.0
 * @date 2021/12/17 10:53
 */
@Component
public class FlowConfigNewTrans {

    @Resource
    private JdbcTemplate jdbcTemplate;

    /**
     * 注入 RedisTemplate 或者 StringRedisTemplate 其中一个即可，前者是后者的父类，它们都已经默认在 Spring 容器中了。
     * org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration 中已经自动将 RedisTemplate 添加到了容器中，直接获取使用即可.
     */
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 为某个区划初始化数据，一个区划下有多个单位，每个单位的用户操作时，它们的区划只能初始化一条。
     * 如果不加处理，高并发下就会出现初始化多条的情况。
     * 1、初始化前先查询，如果已经存在，则直接返回，无需再初始化
     * 2、第一个初始化时进行加锁(往 redis 中插入一个key)，初始化完成后删除 key
     * 3、当出现并发时，其余的如果发现已经存在这个 key，则延迟等待1，然后再次查询是否已经存在，最多循环10次。
     * 4、初始化操作单独新开一个事务，完成后，其余线程立马就能读取到数据，这样就不需要等待整个线程事务提交，
     * 5、获取锁的前后都先查询一次数据.
     *
     * @param mofDivCode
     * @return
     * @throws InterruptedException
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public Map<String, Object> initFlow(String mofDivCode) throws InterruptedException {
        List<Map<String, Object>> mapList = null;
        String cacheKey = "initFlow#" + mofDivCode;

        Map<String, Object> dataMap = new HashMap<>(8);
        dataMap.put("fid", UUID.randomUUID().toString());
        dataMap.put("mof_div_code", mofDivCode);
        dataMap.put("version_start", "2021-12");
        dataMap.put("version_end", "2021-12");
        dataMap.put("agency_code", null);

        //最多循环 max 次，每次延迟 1 秒
        for (int i = 0, max = 10; i < max; i++) {
            mapList = getMaps(mofDivCode, dataMap);
            if (mapList != null && !mapList.isEmpty()) {
                return mapList.get(0);
            }
            if (redisTemplate.opsForValue().setIfAbsent(cacheKey, mofDivCode, 60, TimeUnit.SECONDS)) {
                try {
                    mapList = getMaps(mofDivCode, dataMap);
                    if (mapList != null && !mapList.isEmpty()) {
                        return mapList.get(0);
                    }
                    String sql = "INSERT INTO bas_flow_config VALUES (?, ?, ?, ?, ?)";
                    jdbcTemplate.update(sql, dataMap.values().toArray());
                    System.out.println("初始化数据。");
                    return dataMap;
                } finally {
                    redisTemplate.delete(cacheKey);
                }
            } else {
                //没有获取到锁的用户，等待 1 秒后继续操作
                TimeUnit.MILLISECONDS.sleep(1000);
            }
        }
        return null;
    }

    private List<Map<String, Object>> getMaps(String mofDivCode, Map<String, Object> dataMap) {
        StringBuilder sql = new StringBuilder("select * from bas_flow_config where mof_div_code='").append(mofDivCode).append("'");
        sql.append(" and version_start='").append(dataMap.get("version_start")).append("' and version_end='").append(dataMap.get("version_end")).append("'");
        return jdbcTemplate.queryForList(sql.toString());
    }


}
